﻿using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.Extensions.Logging;
using NP.BPMReportPlatform.AuthorizationUserManager.Dtos;
using NP.BPMReportPlatform.Common.Dtos;
using NP.BPMReportPlatform.DBHelper;
using NP.BPMReportPlatform.Entities.BPM_Platform;
using NP.BPMReportPlatform.Entities.BPM_Process;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;

namespace NP.BPMReportPlatform.AuthorizationUserManager
{
    public class RoleAppService : BPMReportPlatformAppService, IRoleAppService
    {
        private readonly SqlSugarClient _db, _dbPlatform, _dbProcess, _dbEngine;
        private readonly ILogger<UserAppService> _logger;
        public RoleAppService(ISqlSugarAppService sqlSugarAppService, ILogger<UserAppService> logger)
        {
            _db = sqlSugarAppService.GetBusinessInstance();
            _dbPlatform = sqlSugarAppService.GetPlatformInstance();
            _dbProcess = sqlSugarAppService.GetProcessInstance();
            _dbEngine = sqlSugarAppService.GetEngineInstance();
        }

        /// <summary>
        /// 获取角色信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<Role>> GetRoleList(RoleInfoInput input)
        {
            RefAsync<int> totalCount = 0;
            var listQueryable = GetQueryable(input);
            var roleList = await listQueryable.ToPageListAsync(input.PageIndex, input.PageSize, totalCount);
            //返回结果
            return new PagedResultDto<Role>()
            {
                TotalCount = totalCount,
                Items = roleList
            };
        }

        /// <summary>
        /// 导出角色信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<Role>> ExportRoleList(RoleInfoInput input)
        {
            var listQueryable = GetQueryable(input);
            var listViewDto = await listQueryable.ToListAsync();
            return listViewDto;
        }

        private ISugarQueryable<Role> GetQueryable(RoleInfoInput input)
        {
            var listQueryable = _dbProcess.Queryable<Role>()
                .WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name))
                .WhereIF(input.IsEnable.HasValue, x => x.IsEnable == input.IsEnable)
                .WhereIF(input.IsPublic.HasValue, x => x.IsPublic == input.IsPublic)
                .WhereIF(input.RoleType.HasValue, x => x.RoleType == input.RoleType)
                .OrderBy(x => x.CreateDate, OrderByType.Desc);
            return listQueryable;
        }

        /// <summary>
        /// 编辑角色
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<string> EditRoleAsync(Role input)
        {
            if (string.IsNullOrEmpty(input.Name))
            {
                throw new Exception("请填写角色名称");
            }
            RefAsync<string> res = "编辑成功！";
            var entity = input.Id == Guid.Empty ? null : await _dbProcess.Queryable<Role>().FirstAsync(x => x.Id.Equals(input.Id));
            if (entity == null)
            {
                if (_dbProcess.Queryable<Role>().Any(x => x.Name.Equals(input.Name)))
                {
                    throw new Exception("角色已存在");
                }
                entity = new Role
                {
                    Id = Guid.NewGuid(),
                    Name = input.Name,
                    Remark = input.Remark,
                    IsEnable = input.IsEnable,
                    IsPublic = input.IsPublic,
                    CreateUserId = CurrentUser?.Id,
                    CreateDate = DateTime.Now,
                    RoleType = input.RoleType,
                };
                await _dbProcess.Insertable(entity).ExecuteCommandAsync();
                res = "添加成功！";
            }
            else
            {
                if (_dbProcess.Queryable<Role>().Any(x => x.Name.Equals(input.Name) && x.Id != input.Id))
                {
                    throw new Exception("角色已存在");
                }
                entity.Name = input.Name;
                entity.Remark = input.Remark;
                entity.IsEnable = input.IsEnable;
                entity.IsPublic = input.IsPublic;
                entity.CreateUserId = CurrentUser?.Id;
                entity.CreateDate = DateTime.Now;
                entity.RoleType = input.RoleType;
                await _dbProcess.Updateable(entity).WhereColumns(x => x.Id).ExecuteCommandAsync();
            }
            return res;
        }

        /// <summary>
        /// 删除角色
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<string> DeleteRoleAsync(Guid id)
        {
            RefAsync<string> res = "删除成功！";
            var role = await _dbProcess.Queryable<Role>().FirstAsync(x => x.Id == id);
            if (role == null)
            {
                throw new Exception("数据未找到");
            }

            try
            {
                _dbProcess.BeginTran();

                await _dbProcess.Deleteable<AuthorizationProcessesSettings>().Where(x => x.PermissionCode == id.ToString()).ExecuteCommandAsync();
                await _dbProcess.Deleteable<Role>().Where(x => x.Id == id).ExecuteCommandAsync();

                _dbProcess.CommitTran();
            }
            catch (Exception ex)
            {
                _dbProcess.RollbackTran();
                throw new Exception(ex.Message);
            }
            return res;
        }

        /// <summary>
        /// 角色授权流程
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<string> AuthProcess(RoleBindProcessInput input)
        {
            RefAsync<string> res = "授权成功！";
            if (string.IsNullOrEmpty(input.RoleId))
            {
                throw new Exception("角色未找到");
            }
            var role = await _dbProcess.Queryable<Role>().FirstAsync(x => x.Id == Guid.Parse(input.RoleId) && x.IsEnable == 1);
            if (role == null)
            {
                throw new Exception("角色未找到");
            }

            var newBPMOldTreeData = new List<ProcessTree>();
            var oldBPMOldTreeData = new List<ProcessTree>();
            SelectProcessesForTreeData(input.NewProcesses, newBPMOldTreeData);
            SelectProcessesForTreeData(input.OldProcesses, oldBPMOldTreeData);

            var newBPMOldData = await _dbProcess.Queryable<AuthorizationProcessesSettings>()
                .Where(x => x.PermissionCode == input.RoleId && x.PlatformType == 1).ToListAsync();
            var newBPMDelData = newBPMOldData.Where(z => newBPMOldTreeData.Where(x => !x.@checked && x.isleaf).Select(x => x.id).Contains(z.ProcessCode)).ToList();
            var newBPMAddData = newBPMOldTreeData.Where(x => x.@checked && x.isleaf && !newBPMOldData.Select(q => q.ProcessCode).Contains(x.id)).ToList();

            var oldBPMOldData = await _dbProcess.Queryable<AuthorizationProcessesSettings>()
                .Where(x => x.PermissionCode == input.RoleId && x.PlatformType == 2).ToListAsync();
            var oldBPMDelData = oldBPMOldData.Where(z => oldBPMOldTreeData.Where(x => !x.@checked && x.isleaf).Select(x => x.id).Contains(z.ProcessCode)).ToList();
            var oldBPMAddData = oldBPMOldTreeData.Where(x => x.@checked && x.isleaf && !oldBPMOldData.Select(q => q.ProcessCode).Contains(x.id)).ToList();
            try
            {
                _dbProcess.BeginTran();

                if (newBPMDelData.Any())
                {
                    await _dbProcess.Deleteable(newBPMDelData).ExecuteCommandAsync();
                }
                if (newBPMAddData.Any())
                {
                    var newBPMAddDataList = new List<AuthorizationProcessesSettings>();
                    foreach (var data in newBPMAddData)
                    {
                        var entity = new AuthorizationProcessesSettings
                        {
                            Id = Guid.NewGuid().ToString(),
                            ProcessCode = data.id,
                            PermissionCode = input.RoleId,
                            PermissionName = role.Name,
                            Type = 2,
                            PlatformType = 1,
                        };
                        newBPMAddDataList.Add(entity);
                    }
                    await _dbProcess.Insertable(newBPMAddDataList).ExecuteCommandAsync();
                }

                if (oldBPMDelData.Any())
                {
                    await _dbProcess.Deleteable(oldBPMDelData).ExecuteCommandAsync();
                }
                if (oldBPMAddData.Any())
                {
                    var oldBPMAddDataList = new List<AuthorizationProcessesSettings>();
                    foreach (var data in oldBPMAddData)
                    {
                        var entity = new AuthorizationProcessesSettings
                        {
                            Id = Guid.NewGuid().ToString(),
                            ProcessCode = data.id,
                            PermissionCode = input.RoleId,
                            PermissionName = role.Name,
                            Type = 2,
                            PlatformType = 2,
                        };
                        oldBPMAddDataList.Add(entity);
                    }
                    await _dbProcess.Insertable(oldBPMAddDataList).ExecuteCommandAsync();
                }

                _dbProcess.CommitTran();
            }
            catch(Exception ex)
            {
                _dbProcess.RollbackTran();
                throw new Exception(ex.Message);
            }

            return res;
        }

        private void SelectProcessesForTreeData(List<ProcessTree> treeNodes, List<ProcessTree> resps)
        {
            foreach (var node in treeNodes)
            {
                resps.Add(node);
                SelectProcessesForTreeData(node.children, resps);
            }
        }

        /// <summary>
        /// 获取流程数据树结构
        /// </summary>
        /// <returns></returns>
        public async Task<ProcessTreeData> GetAllProcesses(string roleId)
        {
            var res = new ProcessTreeData();
            var auths = await _dbProcess.Queryable<AuthorizationProcessesSettings>().Where(x => x.PermissionCode == roleId).ToListAsync();
            var businessTypes = await _dbProcess.Queryable<BusinessTypes>().Where(x => x.InUse != null && x.InUse.Value)
                .Select(x => new ProcessTree {
                    id = x.BusinessTypeId.ToString(),
                    parentid = (x.ParentId == null || x.ParentId == Guid.Empty) ? "" : x.ParentId.ToString(),
                    name = x.Name,
                    title = x.Name,
                }).ToListAsync();
            var newProcesses = await _dbProcess.Queryable<Processes>().Where(x => x.InUse == 1)
                .Select(x => new ProcessTree
                {
                    id = x.BTID,
                    parentid = x.BusinessTypeId.ToString(),
                    name = x.Name,
                    title = x.Name,
                    isleaf = true,
                }).ToListAsync();
            newProcesses.ForEach(x =>
            {
                x.@checked = auths.Any(q => q.ProcessCode == x.id && q.PlatformType == 1);
            });
            res.NewProcesses = BulidProcessTree(businessTypes, new List<ProcessTree>(), "", newProcesses);
            var newProcessRoot = new ProcessTree
            {
                id = Guid.NewGuid().ToString(),
                parentid = "",
                name = "立邦",
                title = "立邦",
            };
            res.NewProcesses.ForEach(x =>
            {
                x.parentid = newProcessRoot.id;
            });
            newProcessRoot.children = res.NewProcesses;
            res.NewProcesses = new List<ProcessTree> { newProcessRoot };

            var flowCates = await _dbProcess.Queryable<FlowCategory>()
                .Select(x => new ProcessTree
                {
                    id = x.Code,
                    parentid = "",
                    name = x.Name,
                    title = x.Name,
                }).ToListAsync();
            var oldProcesses = await _dbProcess.Queryable<ProcessesOld>().Where(x => x.InUse == 1)
                .Select(x => new ProcessTree
                {
                    id = x.BTID,
                    parentid = x.BusinessTypeName,
                    name = x.Name,
                    title = x.Name,
                    isleaf = true,
                }).ToListAsync();
            oldProcesses.ForEach(x =>
            {
                x.@checked = auths.Any(q => q.ProcessCode == x.id && q.PlatformType == 2);
            });
            flowCates.ForEach(x =>
            {
                x.children = oldProcesses.Where(q => q.parentid == x.id).ToList();
            });
            res.OldProcesses = flowCates;
            var oldProcessRoot = new ProcessTree
            {
                id = Guid.NewGuid().ToString(),
                parentid = "",
                name = "立邦",
                title = "立邦",
            };
            res.OldProcesses.ForEach(x =>
            {
                x.parentid = oldProcessRoot.id;
            });
            oldProcessRoot.children = res.OldProcesses;
            res.OldProcesses = new List<ProcessTree> { oldProcessRoot };

            return res;
        }

        /// <summary>
        /// 使用递归方法建树
        /// </summary>
        private static List<ProcessTree> BulidProcessTree(List<ProcessTree> treeNodes, List<ProcessTree> resps, string pID, List<ProcessTree> processes)
        {
            resps = new List<ProcessTree>();
            List<ProcessTree> tempList = treeNodes.Where(c => c.parentid == pID).ToList();

            for (int i = 0; i < tempList.Count; i++)
            {
                ProcessTree node = new()
                {
                    id = tempList[i].id,
                    btid = tempList[i].btid,
                    parentid = tempList[i].parentid,
                    name = tempList[i].name,
                    title = tempList[i].name,
                    label = tempList[i].name,
                };
                node.children = BulidProcessTree(treeNodes, resps, node.id, processes);
                if (processes.Any(x => x.parentid == node.id))
                {
                    node.children.AddRange(processes.Where(x => x.parentid == node.id));
                }
                resps.Add(node);
            }

            return resps;
        }

        /// <summary>
        /// 角色数据远程搜索
        /// </summary>
        /// <param name="name"></param>
        /// <param name="top"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<Role>> GetRoles(string name, int top = 50)
        {
            var roles = await _dbProcess.Queryable<Role>().WhereIF(!string.IsNullOrWhiteSpace(name), x => x.Name.Contains(name))
                .OrderBy(x => x.CreateDate, OrderByType.Desc).Take(top).ToListAsync();
            return roles;
        }

        /// <summary>
        /// 获取用户绑定的角色
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<Role>> GetUserRoles(string workNumber)
        {
            if (string.IsNullOrEmpty(workNumber))
            {
                throw new Exception("用户未找到");
            }
            var user = await _dbPlatform.Queryable<Users>().FirstAsync(x => x.WorkNumber == workNumber);
            if (user == null)
            {
                throw new Exception("用户未找到");
            }

            var userRoles = await _dbProcess.Queryable<UserRoleMapping, Role>((x, r) => new JoinQueryInfos(JoinType.Inner, x.RoleId == r.Id))
                .Where((x, r) => x.EmployeeCode == workNumber).Select((x, r) => r).ToListAsync();
            return userRoles;
        }

        /// <summary>
        /// 人员绑定角色
        /// </summary>
        /// <returns></returns>
        public async Task<string> AuthUserRole(BindUserRoleInput input)
        {
            RefAsync<string> res = "授权成功！";
            if (string.IsNullOrEmpty(input.workNumber))
            {
                throw new Exception("用户未找到");
            }
            var user = await _dbPlatform.Queryable<Users>().FirstAsync(x => x.WorkNumber == input.workNumber);
            if (user == null)
            {
                throw new Exception("用户未找到");
            }

            var userRoleMaps = await _dbProcess.Queryable<UserRoleMapping>().Where(x => x.EmployeeCode == input.workNumber).ToListAsync();
            var delData = userRoleMaps.Where(x => !input.roleIds.Contains(x.RoleId)).ToList();
            var addData = input.roleIds.Where(x => !userRoleMaps.Select(q => q.RoleId).Contains(x)).ToList();

            try
            {
                _dbProcess.BeginTran();

                if (delData.Any())
                {
                    await _dbProcess.Deleteable(delData).ExecuteCommandAsync();
                }
                if (addData.Any())
                {
                    var entities = new List<UserRoleMapping>();
                    foreach (var data in addData)
                    {
                        entities.Add(new UserRoleMapping
                        {
                            Id = Guid.NewGuid(),
                            RoleId = data,
                            EmployeeCode = input.workNumber,
                        });
                    }
                    await _dbProcess.Insertable(entities).ExecuteCommandAsync();
                }

                _dbProcess.CommitTran();
            }
            catch (Exception ex)
            {
                _dbProcess.RollbackTran();
                throw new Exception(ex.Message);
            }

            return res;
        }

        /// <summary>
        /// 根据工号获取已授权实例分析查询流程数据树结构
        /// </summary>
        /// <returns></returns>
        public async Task<ProcessTreeData> GetAuthAnalysisProcessTree()
        {
            var authProcessCodes = new List<string>();

            var userRole = await _dbProcess.Queryable<Role, UserRoleMapping>((r, ur) =>
               new JoinQueryInfos(JoinType.Inner, r.Id.Equals(ur.RoleId))).
               Where((r, ur) => r.IsEnable == 1 && (ur.EmployeeCode.Equals(CurrentUser.UserName) || r.IsPublic == 1)
               && r.RoleType == (int)RoleEnum.AnalysisInstance).Select(r => r).ToListAsync();

            if (userRole.Any())
            {
                var isAdmin = userRole.Where(r => r.Name.ToLower().Equals("实例分析admin")).Any();
                if (isAdmin)
                {
                    authProcessCodes = await _dbProcess.Queryable<AuthorizationProcessesSettings, Processes>((a, p) =>
                            new JoinQueryInfos(JoinType.Inner, a.ProcessCode.Equals(p.BTID))).
                            Where((a, p) => p.InUse == 1).Select((a, p) => p.BTID).Distinct().ToListAsync();
                }
                else
                {
                    var userRoleArray = userRole.Select(x => x.Id.ToString()).ToList();
                    authProcessCodes = await _dbProcess.Queryable<AuthorizationProcessesSettings, Processes>
                       ((a, p) => new JoinQueryInfos(JoinType.Inner, a.ProcessCode.Equals(p.BTID)))
                       .Where((a, p) => p.InUse == 1).Where((a, p) => SqlFunc.ContainsArray(userRoleArray, a.PermissionCode))
                       .Select((a, p) => p.BTID).Distinct().ToListAsync();
                }
            }

            var res = new ProcessTreeData();
            var businessTypes = await _dbProcess.Queryable<BusinessTypes>().Where(x => x.InUse != null && x.InUse.Value)
                .Select(x => new ProcessTree
                {
                    id = x.BusinessTypeId.ToString(),
                    parentid = (x.ParentId == null || x.ParentId == Guid.Empty) ? "" : x.ParentId.ToString(),
                    name = x.Name,
                    title = x.Name,
                    label = x.Name
                }).ToListAsync();
            var newProcesses = await _dbProcess.Queryable<Processes>().Where(x => x.InUse == 1)
                .Select(x => new ProcessTree
                {
                    id = x.ProcessId.ToString(),
                    btid = x.BTID,
                    parentid = x.BusinessTypeId.ToString(),
                    name = x.Name,
                    title = x.Name,
                    label = x.Name,
                    isleaf = true,
                }).ToListAsync();
            newProcesses.ForEach(x =>
            {
                x.@checked = authProcessCodes.Any(q => q == x.btid);
            });
            var leafNodes = newProcesses.Where(x => x.@checked).ToList();
            res.NewProcesses = BulidProcessTreeForLeaf(businessTypes, leafNodes);
            var newProcessRoot = new ProcessTree
            {
                id = Guid.NewGuid().ToString(),
                parentid = "",
                name = "立邦",
                title = "立邦",
                label = "立邦"
            };
            res.NewProcesses.ForEach(x =>
            {
                x.parentid = newProcessRoot.id;
            });
            newProcessRoot.children = res.NewProcesses;
            res.NewProcesses = new List<ProcessTree> { newProcessRoot };

            return res;
        }

        /// <summary>
        /// 从叶子节点向上构建树结构
        /// </summary>
        /// <returns></returns>
        private List<ProcessTree> BulidProcessTreeForLeaf(
            List<ProcessTree> allNodes, 
            List<ProcessTree> leafNodes)
        {
            var currentNodes = new List<ProcessTree>();
            foreach (var leafNode in leafNodes)
            {
                var topNode = GetParentTree(allNodes, leafNode);
                if (topNode != null && !currentNodes.Any(x => x.id == topNode.id))
                {
                    currentNodes.Add(topNode);
                }
            }
            return currentNodes;
        }

        private ProcessTree GetParentTree(List<ProcessTree> allNodes, ProcessTree node)
        {
            if (string.IsNullOrEmpty(node.parentid))
            {
                return node;
            }
            var parentNode = allNodes.FirstOrDefault(x => x.id == node.parentid);
            if (parentNode != null)
            {
                if (!parentNode.children.Any(x => x.id == node.id))
                {
                    parentNode.children.Add(node);
                }
                return GetParentTree(allNodes, parentNode);
            }
            else
            {
                return node.isleaf ? null : node;
            }
        }

        public async Task<ProcessTreeData> GetAuthAnalysisProcessTreeForThirdFlow()
        {
            var authProcessCodes = await _dbProcess.Queryable<Processes, ProcessResponsible>(
                (x, p) => x.ProcessId == p.ProcessId).Where((x, p) => x.InUse == 1 &&
                (p.FormType == "ThirdCommonProcess" || p.FormType == "DDMThirdCommonProcess" || p.FormType == "ThirdPagingTableLogic"))
                .Select((x, p) => x.BTID).ToListAsync();

            var res = new ProcessTreeData();
            var businessTypes = await _dbProcess.Queryable<BusinessTypes>().Where(x => x.InUse != null && x.InUse.Value)
                .Select(x => new ProcessTree
                {
                    id = x.BusinessTypeId.ToString(),
                    parentid = (x.ParentId == null || x.ParentId == Guid.Empty) ? "" : x.ParentId.ToString(),
                    name = x.Name,
                    title = x.Name,
                    label = x.Name
                }).ToListAsync();
            var newProcesses = await _dbProcess.Queryable<Processes>().Where(x => x.InUse == 1)
                .Select(x => new ProcessTree
                {
                    id = x.ProcessId.ToString(),
                    btid = x.BTID,
                    parentid = x.BusinessTypeId.ToString(),
                    name = x.Name,
                    title = x.Name,
                    label = x.Name,
                    isleaf = true,
                }).ToListAsync();
            newProcesses.ForEach(x =>
            {
                x.@checked = authProcessCodes.Any(q => q == x.btid);
            });
            var leafNodes = newProcesses.Where(x => x.@checked).ToList();
            res.NewProcesses = BulidProcessTreeForLeaf(businessTypes, leafNodes);
            var newProcessRoot = new ProcessTree
            {
                id = Guid.NewGuid().ToString(),
                parentid = "",
                name = "立邦",
                title = "立邦",
                label = "立邦"
            };
            res.NewProcesses.ForEach(x =>
            {
                x.parentid = newProcessRoot.id;
            });
            newProcessRoot.children = res.NewProcesses;
            res.NewProcesses = new List<ProcessTree> { newProcessRoot };

            return res;
        }
    }
}
